// Copyright 2018 The Go Authors. All rights reserved.
// Use of this session code is governed by a BSD-style
// license that can be found in the LICENSE file.

package lsp

import (
	"context"
	"mi/compiler/diagnostic"
	"mi/internal/event"
	"mi/internal/lsp/debug/tag"
	"mi/internal/lsp/protocol"
	"mi/internal/span"
	"strings"
	"time"
)

var seq = 0
var version float64 = 0
func (s *Server) diagnose(ctx context.Context) error {

	seq ++
	cur := seq
	go func() {
		<-time.After(time.Millisecond * 350)
		if cur == seq {
			diags, err := s.session.GetDiagnostics(ctx)
			if err != nil {
				return
			}

			version ++
			for uri, diagnostics := range diags {
				pdiags, err := toProtocolDiagnostics(diagnostics)
				if err != nil {
					return
				}
				if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
					Diagnostics: pdiags,
					URI:         protocol.URIFromSpanURI(uri),
					Version:     version,
				}); err == nil {
					// return err
				} else {
					if ctx.Err() != nil {
						// Publish may have failed due to a cancelled context.
						return
					}
					event.Error(ctx, "publishReports: failed to deliver diagnostic", err, tag.URI.Of(uri))
				}
			}
		}
	}()


	return nil
}

func toProtocolDiagnostics(diagnostics []*diagnostic.Diagnostic) ([]protocol.Diagnostic,error) {
	reports := make([]protocol.Diagnostic, 0)
	for _, diag := range diagnostics {

		var file = diag.File
		u16span, err := span.UTF16SpanFromOffsetWithCache(diag.Start,
			diag.Start + diag.Length, file.Text, file.GetLineStarts())
		if err != nil {
			return nil, err
		}

		var message string
		if diag.MessageChain == nil {
			message = diag.MessageText
		} else {
			message = diag.MessageChain.MessageText
		}

		reports = append(reports, protocol.Diagnostic{
			// diagnostic.Information might start with \n or \t
			Message:            strings.TrimSpace(message),
			Range:              toProtocolRange(u16span),
			Severity:           convertDiagnosticSeverity(diag.Category),
		})
	}
	return reports, nil
}

func convertDiagnosticSeverity(category diagnostic.Kind) protocol.DiagnosticSeverity {
	switch category {
	case diagnostic.Information:
		return protocol.SeverityHint
	case diagnostic.Warning:
		return protocol.SeverityWarning
	case diagnostic.Error:
		return protocol.SeverityError
	}
	panic("category is unknown")
}